using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;

namespace WozLib.UI.Controls
{
  /// <summary>
  /// Interaction logic for ScrollNavigator.xaml
  /// </summary>

  public partial class ScrollNavigator : System.Windows.Controls.UserControl
  {



    public ScrollViewer ScrollViewer
    {
      get 
      { 
        return (ScrollViewer)GetValue(ScrollViewerProperty); 
      }

      set 
      { 
        SetValue(ScrollViewerProperty, value);
      }
    }

    // Using a DependencyProperty as the backing store for ScrollViewer.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ScrollViewerProperty =
        DependencyProperty.Register("ScrollViewer", typeof(ScrollViewer), typeof(ScrollNavigator), new PropertyMetadata(SomePropertyChanged));

    static void SomePropertyChanged(DependencyObject dObj, DependencyPropertyChangedEventArgs args)
    {
      if (dObj is ScrollNavigator)
      {
        ScrollNavigator scrollNavigator = dObj as ScrollNavigator;

        if (args.Property == ScrollViewerProperty)
        {
          if (args.NewValue != null)
          {
            // WATCH OUT HERE!!!! Don't want to be subscribed to many of these bad boys!
            scrollNavigator.ScrollViewer.SizeChanged += new SizeChangedEventHandler(scrollNavigator.ScrollViewer_SizeChanged);
            scrollNavigator.ScrollViewer.ScrollChanged += new ScrollChangedEventHandler(scrollNavigator.ScrollViewer_ScrollChanged);
          }

          scrollNavigator.UpdateScrollCropRectangle();
        }
      }
    }

    public ScrollNavigator()
    {
      // Wait to mess with UI until we are loaded
      this.Loaded += new RoutedEventHandler(ScrollNavigator_Loaded);

      InitializeComponent();
    }

    void ScrollNavigator_Loaded(object sender, RoutedEventArgs e)
    {
      // Subscribe to all the UI events we need to process
      _MacroViewCanvas.PreviewMouseDown += new MouseButtonEventHandler(_MacroViewCanvas_PreviewMouseDown);
      _MacroViewCanvas.PreviewMouseMove += new MouseEventHandler(_MacroViewCanvas_PreviewMouseMove);
      _MacroViewCanvas.SizeChanged += new SizeChangedEventHandler(_MacroViewCanvas_SizeChanged);
    }

    private Point _mouseDownPointRelativeTo_MacroViewCanvas;
    private Point _mouseDownScrollOffset;

    //private Point _mouseDown_ScrollCropRectanglePointRelativeTo_MacroViewCanvas;
    //private bool _isUpdatingScrollCropRectangle = false;
    //private object _updatingScrollCropRectanglePadlock = new object();
    //lock (_updatingScrollCropRectanglePadlock)
    //{
    //  _isUpdatingScrollCropRectangle = true;
    //  _isUpdatingScrollCropRectangle = false;
    //}

    void _MacroViewCanvas_PreviewMouseMove(object sender, MouseEventArgs e)
    {
      // if mouse is down try to move the _ScrollCropRectangle in relation to were the mouse is now
      try
      {
        // if the scrollviewer exists and it's content requires scrolling in at least one direction
        // scroll the ScrollViewer based on mouse delta
        if (e.LeftButton == MouseButtonState.Pressed && ScrollViewer != null && (ScrollViewer.ScrollableHeight > 0 || ScrollViewer.ScrollableWidth > 0))
        {
          // ratio of content's width and height
          double extentXYRatio = ScrollViewer.ExtentWidth / ScrollViewer.ExtentHeight;

          double canvasXYRatio = _MacroViewCanvas.ActualWidth / _MacroViewCanvas.ActualHeight;

          Rect thumbnailRect = new Rect();

          if (canvasXYRatio < extentXYRatio)
          {
            // we are constrained in the width dimension
            double height = _MacroViewCanvas.ActualWidth / extentXYRatio;
            double top = (_MacroViewCanvas.ActualHeight - height) / 2;
            thumbnailRect = new Rect(new Point(0, top), new Size(_MacroViewCanvas.ActualWidth, height));
          }
          else
          {
            // we are constrained in the height dimension
            double width = extentXYRatio * _MacroViewCanvas.ActualHeight;
            double left = (_MacroViewCanvas.ActualWidth - width) / 2;
            thumbnailRect = new Rect(new Point(left, 0), new Size(width, _MacroViewCanvas.ActualHeight));
          }

          Point mouseLocation = e.GetPosition(_MacroViewCanvas);
          double xCanvasDelta = _mouseDownPointRelativeTo_MacroViewCanvas.X - mouseLocation.X;
          double yCanvasDelta = _mouseDownPointRelativeTo_MacroViewCanvas.Y - mouseLocation.Y;

          double xExtentDelta = xCanvasDelta * ScrollViewer.ExtentWidth / thumbnailRect.Width;
          double yExtentDelta = yCanvasDelta * ScrollViewer.ExtentHeight / thumbnailRect.Height;

          // I wish we could do this in a single step
          ScrollViewer.ScrollToHorizontalOffset(_mouseDownScrollOffset.X - xExtentDelta);
          ScrollViewer.ScrollToVerticalOffset(_mouseDownScrollOffset.Y - yExtentDelta);
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine(ex.ToString());
      } 
    }

    void _MacroViewCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
      try
      {
        if (ScrollViewer != null)
        {
          // Record start position
          _mouseDownPointRelativeTo_MacroViewCanvas = e.GetPosition(_MacroViewCanvas);
          _mouseDownScrollOffset = new Point(ScrollViewer.HorizontalOffset, ScrollViewer.VerticalOffset);
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine(ex.ToString());
      }
    }

    private void UpdateScrollCropRectangle()
    {
      try
      {
        // if the scrollviewer exists and it's content requires scrolling in at least one direction
        // we show our rectangle
        if (ScrollViewer != null && (ScrollViewer.ScrollableHeight > 0 || ScrollViewer.ScrollableWidth > 0))
        {
          // make our overlay rectangle visible
          _ScrollCropRectangle.Visibility = Visibility.Visible;

          // ratio of viewing area's width and height
          double viewportXYRatio = ScrollViewer.ViewportWidth / ScrollViewer.ViewportHeight;

          // ratio of content's width and height
          double extentXYRatio = ScrollViewer.ExtentWidth / ScrollViewer.ExtentHeight;

          double canvasXYRatio = _MacroViewCanvas.ActualWidth / _MacroViewCanvas.ActualHeight;

          Rect thumbnailRect = new Rect();

          if (canvasXYRatio < extentXYRatio)
          {
            // we are constrained in the width dimension
            double height = _MacroViewCanvas.ActualWidth / extentXYRatio;
            double top = (_MacroViewCanvas.ActualHeight - height) / 2;
            thumbnailRect = new Rect(new Point(0, top), new Size(_MacroViewCanvas.ActualWidth, height));
          }
          else
          {
            // we are constrained in the height dimension
            double width = extentXYRatio * _MacroViewCanvas.ActualHeight;
            double left = (_MacroViewCanvas.ActualWidth - width) / 2;
            thumbnailRect = new Rect(new Point(left, 0), new Size(width, _MacroViewCanvas.ActualHeight));
          }

          if (thumbnailRect.Width != 0)
          {
            // update our _ScrollCropRectangle to be the right size and proportions
            double widthRatio = ScrollViewer.ViewportWidth / ScrollViewer.ExtentWidth;
            double width = (widthRatio >= 1 ? thumbnailRect.Width : widthRatio * thumbnailRect.Width);

            double heightRatio = ScrollViewer.ViewportHeight / ScrollViewer.ExtentHeight;
            double height = (heightRatio >= 1 ? thumbnailRect.Height : heightRatio * thumbnailRect.Height);

            _ScrollCropRectangle.Width = width;
            _ScrollCropRectangle.Height = height;


            // update the location of the _ScrollCropRectangle
            double leftOffset = ScrollViewer.HorizontalOffset / ScrollViewer.ExtentWidth * thumbnailRect.Width;
            double topOffset = ScrollViewer.VerticalOffset / ScrollViewer.ExtentHeight * thumbnailRect.Height;

            _ScrollCropRectangle.SetValue(Canvas.LeftProperty, thumbnailRect.Left + leftOffset);
            _ScrollCropRectangle.SetValue(Canvas.TopProperty, thumbnailRect.Top + topOffset);
          }
        }
        else
        {
          // make our overlay rectangle invisible
          _ScrollCropRectangle.Visibility = Visibility.Hidden;
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine(ex.ToString());
      }
    }

    void ScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      UpdateScrollCropRectangle();
    }

    private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
      UpdateScrollCropRectangle();
    }

    void _MacroViewCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      UpdateScrollCropRectangle();
    }
  }
}